package com.logic.landseaserver.service.bill.rerent;

import com.logic.common.security.SecurityUtils;
import com.logic.landseaserver.common.LandeaConstants;
import com.logic.landseaserver.common.LandseaErrorMessageEnum;
import com.logic.landseaserver.common.enums.BillStatusEnum;
import com.logic.landseaserver.common.enums.BillTypeEnum;
import com.logic.landseaserver.common.enums.ContractStatusCodeEnum;
import com.logic.landseaserver.common.enums.ExpensesStatusEnum;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.util.DateUtil;
import com.logic.landseaserver.common.util.LandSeaUtil;
import com.logic.landseaserver.domain.*;
import com.logic.landseaserver.persistence.read.*;
import com.logic.landseaserver.persistence.write.PreOffrentBillWriteMapper;
import com.logic.landseaserver.persistence.write.PreOffrentDetailBillWriteMapper;
import com.logic.landseaserver.ws.dto.CheckFeeListDTO;
import com.logic.landseaserver.ws.dto.ContractMonthFeeDTO;
import com.logic.system.service.IConfigurationService;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * Created by Junny on 2017/11/23.
 * 结转单
 */
@Service
public class OverBillService{

    private static final Logger LOG = LoggerFactory.getLogger(OverBillService.class);

    @Autowired
    private PreOffrentBillWriteMapper preOffrentBillWriteMapper;

    @Autowired
    private PreOffrentDetailBillWriteMapper preOffrentDetailBillWriteMapper;

    @Autowired
    private BillDetailReadMapper billDetailReadMapper;

    @Autowired
    private MonthlyBillReadMapper monthlyBillReadMapper;

    @Autowired
    private BillReadMapper billReadMapper;

    @Autowired
    private CheckFeeListReadMapper checkFeeListReadMapper;

    @Autowired
    private ExpensesReadMapper expensesReadMapper;

    @Autowired
    private ContractReadMapper contractReadMapper;

    @Autowired
    private IConfigurationService configurationService;

    public Integer createOverBill(Contract contract) throws LandseaException{
        //不允许存在部分支付的账单
        List<Bill> receivedHalfList = billReadMapper.queryBillForCIdStatus(contract.getId(), BillStatusEnum.BIST1.getCode());
        if (receivedHalfList.size()>0) {
            throw LandseaException.createException(LandseaErrorMessageEnum.RERENT_BILL_HALF_PAY);
        }
        List<Bill> noPayList = billReadMapper.queryBillForCIdStatus(contract.getId(), BillStatusEnum.BIST3.getCode());
        if(noPayList.size()>0){
            throw LandseaException.createException(LandseaErrorMessageEnum.RERENT_BILL_NO_PAY);
        }
        List<PreOffrentDetailBill> preOffrentDetailBills = new ArrayList<>();
        //续租操作日期
        Date reRentDate = new Date();
        //逾期续租 每天收取滞纳金（房租的x%）
        int delayFlag = DateUtil.compareTime(reRentDate,contract.getOutDate());
        if(delayFlag>0){
            //逾期退房
            //计算合同结束日至目前共有x天
            int inDay =  DateUtil.getInstence().differentDaysByMillisecond(contract.getOutDate(),reRentDate)-1;
            //收取滞纳金额百分比
            Double persentage = configurationService.getDoubleValue("SYS_PRO_OF_OVE_FIN");
            Double avrMonthDays =  configurationService.getDoubleValue("SYS_MONTHLY_AVA_DAYS");
            if (persentage>0) {
                Double lateAmout = LandSeaUtil.get2Double(contract.getSalePrice()*persentage*inDay);
                PreOffrentDetailBill lateFee = new PreOffrentDetailBill();
                lateFee.setPreType(BillTypeEnum.CTBI13.getCode());
                lateFee.setBillAmount(lateAmout);
                lateFee.setDateBegin(DateUtils.addDays(contract.getOutDate(),1));
                lateFee.setDateEnd(reRentDate);
                preOffrentDetailBills.add(lateFee);
            }
            //计算逾期期间房租服务费
            Double hoursePrice =  LandSeaUtil.getIntDouble(contract.getSalePrice()/avrMonthDays*inDay);
            PreOffrentDetailBill hourseFee = new PreOffrentDetailBill();
            hourseFee.setPreType(BillTypeEnum.CTBI8.getCode());
            hourseFee.setBillAmount(hoursePrice);
            hourseFee.setDateBegin(DateUtils.addDays(contract.getOutDate(),1));
            hourseFee.setDateEnd(reRentDate);
            preOffrentDetailBills.add(hourseFee);

            Double servicePrice =  LandSeaUtil.getIntDouble(contract.getPropertyFee()/avrMonthDays*inDay);
            PreOffrentDetailBill serviceFee = new PreOffrentDetailBill();
            serviceFee.setPreType(BillTypeEnum.CTBI9.getCode());
            serviceFee.setBillAmount(servicePrice);
            serviceFee.setDateBegin(DateUtils.addDays(contract.getOutDate(),1));
            serviceFee.setDateEnd(reRentDate);
            preOffrentDetailBills.add(serviceFee);
        }

        List<String> ckFees = new ArrayList<>();
        List<BillDetail> lastPayBillDts = queryLastPayBillDetail(contract.getId());
        List<PreOffrentDetailBill> cfeeLists = checkFeeLists(contract, lastPayBillDts,ckFees);
        //获取未缴的其他费项
        List<MonthlyBill> monthlyBills = queryMonthyNoPayBill(contract,ckFees);

        //将未缴其他 费项加入至 predetailbill
        if(monthlyBills!=null){
            for(MonthlyBill monthlyBill:monthlyBills){
                PreOffrentDetailBill mdb = new PreOffrentDetailBill();
                mdb.setPreType(monthlyBill.getExpenseType());
                mdb.setConsumption(monthlyBill.getConsumption());
                mdb.setBillAmount(monthlyBill.getBillPrice());
                mdb.setDateBegin(monthlyBill.getStartTime());
                mdb.setDateEnd(monthlyBill.getEndTime());
                preOffrentDetailBills.add(mdb);
            }
        }
        //用量 水->金额
        //将交割单加入至predetailbill
        if(cfeeLists!=null){
            for(PreOffrentDetailBill pdb:cfeeLists){
                preOffrentDetailBills.add(pdb);
            }
        }
        int preBillId = createPreBill(preOffrentDetailBills,contract,delayFlag);
        for(PreOffrentDetailBill pdb:preOffrentDetailBills){
            pdb.setPrebillId(preBillId);
            pdb.setCouponFlag(false);
            pdb.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
        }
        preOffrentDetailBillWriteMapper.insertList(preOffrentDetailBills);
        return preBillId;
    }

    private Integer createPreBill(List<PreOffrentDetailBill> preOffrentDetailBills,Contract contract, Integer delayFlag){
        //退租日期
        Date offrentDate = new Date();
        Double totalAmount=0.0;
        //获取水费押金
        Double waterDpsAmount = getWaterDeposit(preOffrentDetailBills, contract);
        //房屋押金
        Double hourseDpsAmount = getHourseDeposit(preOffrentDetailBills, contract);
        if(preOffrentDetailBills!=null){
            for(PreOffrentDetailBill pod:preOffrentDetailBills){
                totalAmount = totalAmount+pod.getBillAmount();
                pod.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
            }
        }
        PreOffrentBill preBill = new PreOffrentBill();
        preBill.setContractId(contract.getId());
        preBill.setContractNo(contract.getContractNo());
        preBill.setBillMonth(DateUtil.getInstence().convertYM(new Date()));
        preBill.setRefundAmount(LandSeaUtil.get2Double(totalAmount));
        preBill.setDamagesAmount(0.0);
        preBill.setOffrentType(LandeaConstants.OffrentType.RERENT);//续租退房
        if(delayFlag>0){
            preBill.setOffrentType(LandeaConstants.OffrentType.DELAY_RENT);//逾期续租
        }
        preBill.setDepositFlag(true);
        preBill.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
        preOffrentBillWriteMapper.insertSelective(preBill);
        return preBill.getId();
    }

    public Double getWaterDeposit(List<PreOffrentDetailBill> preOffrentDetailBills, Contract contract) {
        Double waterDepositFee = -contract.getWaterDeposit();
        PreOffrentDetailBill waterDeposit = new PreOffrentDetailBill();
        waterDeposit.setDateBegin(contract.getInDate());
        waterDeposit.setDateEnd(contract.getOutDate());
        waterDeposit.setBillAmount(waterDepositFee);
        waterDeposit.setPreType(BillTypeEnum.CTBI10.getCode());
        preOffrentDetailBills.add(waterDeposit);
        return waterDepositFee;
    }

    //房屋押金
    public Double getHourseDeposit(List<PreOffrentDetailBill> preOffrentDetailBills, Contract contract) {
        Double hourseDepositFee = -contract.getDeposit();
        PreOffrentDetailBill hourseDeposit = new PreOffrentDetailBill();
        hourseDeposit.setDateBegin(contract.getInDate());
        hourseDeposit.setDateEnd(contract.getOutDate());
        hourseDeposit.setBillAmount(hourseDepositFee);
        hourseDeposit.setPreType(BillTypeEnum.CTBI11.getCode());
        preOffrentDetailBills.add(hourseDeposit);
        return hourseDepositFee;
    }

    public List<BillDetail> queryLastPayBillDetail(int contractId) {
        //取出该合同缴费过的所有类型
        List<String> feeTypes = billDetailReadMapper.queryFeeTypeByContract(contractId);
        //取出上一次缴费记录
        HashMap queryMap = new HashMap();
        queryMap.put("types", feeTypes);
        queryMap.put("contractId", contractId);
        queryMap.put("payFlag", true);
        return billDetailReadMapper.queryLastBillType(queryMap);
    }

    public List<PreOffrentDetailBill> checkFeeLists(Contract contract, List<BillDetail> billDetails, List<String> ckFees)throws LandseaException {
        List<PreOffrentDetailBill> preBillDetails= new ArrayList<>();
        //计量类
        Map<String,Object> queryParam = new HashMap<String,Object>();
        queryParam.put("contractId", contract.getId());
        queryParam.put("checkType", LandeaConstants.CheckType.UN_RENT);
        List<CheckFeeListDTO> feeOutList = checkFeeListReadMapper.selectByContractIdAndCheckType(queryParam);
        queryParam.put("checkType",  LandeaConstants.CheckType.IN_COMING);
        List<CheckFeeListDTO> feeInList = checkFeeListReadMapper.selectByContractIdAndCheckType(queryParam);
        if(feeInList==null){
            throw LandseaException.createException(LandseaErrorMessageEnum.OFFRENT_CHECKIN_FEELIST_ERROR);
        }
        List<ContractMonthFeeDTO> contractMonthFees = contractReadMapper.queryContractFee(contract.getId(), ContractStatusCodeEnum.SIGN.getCode());
        ContractMonthFeeDTO contractMonthFeeDTO = null;
        if(contractMonthFees.size()>0){
            contractMonthFeeDTO = contractMonthFees.get(0);
        }
        //获取计量类上次抄表日期
        if(feeOutList!=null){
            for(CheckFeeListDTO fee:feeOutList){
                String type= fee.getType();
                ckFees.add(type);
                Optional<BillDetail> billDt =billDetails.stream().filter(e -> e.getType().equals(type)).findFirst();
                Expenses lastEx= null;
                String feeStartTime = null;
                PreOffrentDetailBill preDetail = new PreOffrentDetailBill();
                if(billDt.isPresent()){//有交过计量类数据 从运营数据中获取
                    feeStartTime=DateUtil.getInstence().convertDate(billDt.get().getDateEnd());
                    HashMap qMap=new HashMap();
                    qMap.put("roomId",contract.getRoomId());
                    qMap.put("status", ExpensesStatusEnum.ES3.getCode());
                    qMap.put("type",type);
                    qMap.put("inDate",feeStartTime);
                    lastEx = expensesReadMapper.querExpenses(qMap);
                    preDetail.setDateBegin(DateUtils.addDays(DateUtil.getInstence().convertDate(feeStartTime),1));
                }else{//从入住交割单获取信息
                    Optional<CheckFeeListDTO> inFee =feeInList.stream().filter(e -> e.getType().equals(type)).findFirst();
                    if(inFee.isPresent()){
                        feeStartTime = DateUtil.getInstence().convertDate(inFee.get().getInDate());
                        lastEx = new Expenses();
                        lastEx.setDegree(inFee.get().getConsumptionEnd());
                        preDetail.setDateBegin(DateUtil.getInstence().convertDate(feeStartTime));
                    }
                }
                if(lastEx!=null){
                    preDetail.setDateEnd(fee.getInDate());
                    preDetail.setPreType(type);
                    Double consum =LandSeaUtil.get2Double(fee.getConsumptionEnd()-lastEx.getDegree());
                    preDetail.setConsumption(consum);
                    preDetail.setBillAmount(calculationPrice(type,contractMonthFeeDTO,consum));
                    preBillDetails.add(preDetail);
                }else{
                    LOG.info("has not query last exp indata,contract id is "+contract.getId()+" fee type is :"+type);
                    continue;
                }
            }
        }else{
            throw LandseaException.createException(LandseaErrorMessageEnum.CONTRACT_IS_NULL);
        }
        return preBillDetails;
    }

    public List<MonthlyBill> queryMonthyNoPayBill(Contract contract, List<String> ckFees) {
        //退房时获取管家店长录入的未缴其余费项
        List<MonthlyBill> monthlyBills = new ArrayList<>();
        List<MonthlyBill> mthBills = monthlyBillReadMapper.queryMonthlyBillByStatus(contract.getId(), BillStatusEnum.BIST3.getCode());
        if(mthBills!=null){
            Boolean checked  =  ckFees.size()>0 ? true :false;
            for(MonthlyBill bill:mthBills){
                //排除计量类的
                if(checked){
                    if(ckFees.contains(bill.getExpenseType())){
                        continue;
                    }
                }
                monthlyBills.add(bill);
            }
        }
        return monthlyBills;
    }

    private Double calculationPrice(String feeType, ContractMonthFeeDTO cmDto, Double consumption){
        Double billPrice = 0.0;
        //计算水
        if(feeType.equals(BillTypeEnum.CTBI1.getCode())){
            return LandSeaUtil.get2Double(consumption*cmDto.getWaterPrice());
        }else if(feeType.equals(BillTypeEnum.CTBI2.getCode())){
            return LandSeaUtil.get2Double(consumption*cmDto.getElectricPrice());
        }else if(feeType.equals(BillTypeEnum.CTBI3.getCode())){
            return LandSeaUtil.get2Double(consumption*cmDto.getGasPrice());
        }else if(feeType.equals(BillTypeEnum.CTBI4.getCode())){
            return  LandSeaUtil.get2Double(consumption*cmDto.getHotWaterPrice());
        }else{
            return billPrice;
        }
    }
}
